SageMaker Ground Truthのジョブの仕組みを調査した
NTT東日本の中村です。
SageMaker Ground Truthのラベリングジョブを管理しているのですが、タスクの配分とジョブの連鎖について調査してみました。
概要
社内のプロジェクトでSageMaker Ground Truthのラベリングジョブを実行しており、同じようなテスト環境を構築しました。
よくある「静止画をまとめてアップロードする」手法ではなく、動画フレームから静止画をスライスし、ラベル付けを行っています。
- Amazon Cognitoを利用した Private WorkForce(三人)
- データソースに3つの動画を用意し、S3にアップロードしておく
- Ground Truthにより動画から静止画フレームを抽出し、静止画フレームに対してオブジェクト検出(バウンディングボックス)の矩形を設定する
- この時、どのようなジョブの割当が行われるかをチェックする
- ジョブを停止して、ジョブのチェインを行った時、どのように前回のジョブの情報が引き継がれるかをチェックする
動画を用意し、ジョブを作成する
100秒の動画を3つ用意しました。
それぞれ、1から100、101から200、201から300まで、1秒毎にカウントが増えていきます。
S3にアップロードします。
Ground Truthでジョブを作成し、3動画分のフレームの抽出を行います。 それぞれ秒間60フレームの動画なので、60フレームごとに抽出することで、100枚の静止画を切り出します。
ここで「すべてのフレーム」を使用すると、60フレーム×100秒=60000枚の静止画を切り出そうとして、2000枚のクォータに引っかかるので、ご注意下さい。
データのセットアップ(フレーム抽出とmanifestの作成)が完了すると、動画と同名のフォルダが作成されます。
動画のフォルダを開くと、スライスした静止画が保存されています。
countdownフォルダ直下のmanifestです。ジョブ内で使われる3つの動画を管理しています。
{"source-ref":"s3://hogehoge/copy/countdown/101to200.mp4/101to200.mp4-sequence-1715219761.json"} {"source-ref":"s3://hogehoge/copy/countdown/1to100.mp4/1to100.mp4-sequence-1715219761.json"} {"source-ref":"s3://hogehoge/copy/countdown/201to300.mp4/201to300.mp4-sequence-1715219761.json"}
countdownフォルダ直下のmanifest.json
{"document-version":"2018-11-28","labels":[{"label":"nested"},{"label":"nested2"}]}
3つの動画のうち、1-100の動画フォルダ内のjsonファイルです。
フォルダ内に保存された静止画を管理しています。
{ "seq-no": 2, "prefix": "s3://hogehoge/copy/countdown/1to100.mp4/", "number-of-frames": 100, "frames": [ { "frame-no": 0, "frame": "frame_0000.jpeg" }, //中略・・・ { "frame-no": 99, "frame": "frame_0099.jpeg" } ] }
ジョブの作成が完了すると、 ジョブと同名のフォルダ(今回はcowntdown-team)が作成されます。
ここに、ラベル付けされたデータが格納されます。
3つの動画のスライスを持つジョブを作成後、Private WorkForceに設定された3ユーザでログインし、タスクを開始してみます。
ユーザ毎に異なる動画が割り当てられました。
タスクの実施・ジョブの完了
今回のタスクは「画面内に『3』があった場合に矩形で囲む」となります。 ユーザ毎に異なる進捗を与えてみます。
- test1(101-200):「3」が存在する静止画を全てラベル付けし、タスクを完了する
- test2(201-300):「3」が存在する静止画を途中(250)までラベル付けし、タスクを完了する
- test3(1-100):何もしない(タスクの完了もしない)
test1、test2ユーザは予定の進捗までラベル付けを進めてから、タスクを完了します。
その後、手動でジョブを終了させます。ステータスが停止になり、ラベル付けの進捗が2/3になっているのが分かります。
何もしていないtest3ユーザはカウントされていません。
cowntdown-teamフォルダ配下に、実施したラベル付けの情報が格納されています。
ざっと構造を確認すると、annotations/worker-responseフォルダ配下に、ラベル付けされたデータが格納されていることが解ります。
iteration-1の0がtest1ユーザ、iteration-1の2がtest2ユーザです。
├── annotation-tool │ └── data.json ├── annotations │ ├── consolidated-annotation │ │ └── output │ │ ├── 0 │ │ │ └── SeqLabel.json │ │ └── 2 │ │ └── SeqLabel.json │ └── worker-response │ └── iteration-1 │ ├── 0 │ │ ├── 2024-05-09_02:19:45.json │ │ └── 2b830c9769c82118aa0963d7c1e03044ba23abff │ │ ├── frame_0002.jpeg.json │ │ ├── frame_0013.jpeg.json │ │ ├── frame_0023.jpeg.json │ │ ├── frame_0030.jpeg.json │ │ ├── frame_0031.jpeg.json │ │ ├── frame_0032.jpeg.json │ │ ├── frame_0033.jpeg.json │ │ ├── frame_0034.jpeg.json │ │ ├── frame_0035.jpeg.json │ │ ├── frame_0036.jpeg.json │ │ ├── frame_0037.jpeg.json │ │ ├── frame_0038.jpeg.json │ │ ├── frame_0039.jpeg.json │ │ ├── frame_0043.jpeg.json │ │ ├── frame_0053.jpeg.json │ │ ├── frame_0063.jpeg.json │ │ ├── frame_0073.jpeg.json │ │ ├── frame_0083.jpeg.json │ │ └── frame_0093.jpeg.json │ └── 2 │ ├── 2024-05-09_02:21:51.json │ └── 71b5ea23b79c9b213dd766aeed973fe722922b9b │ ├── frame_0002.jpeg.json │ ├── frame_0012.jpeg.json │ ├── frame_0022.jpeg.json │ ├── frame_0029.jpeg.json │ ├── frame_0030.jpeg.json │ ├── frame_0031.jpeg.json │ ├── frame_0032.jpeg.json │ ├── frame_0033.jpeg.json │ ├── frame_0034.jpeg.json │ ├── frame_0035.jpeg.json │ ├── frame_0036.jpeg.json │ ├── frame_0037.jpeg.json │ ├── frame_0038.jpeg.json │ └── frame_0042.jpeg.json ├── manifests │ ├── intermediate │ │ └── 1 │ │ └── output.manifest │ └── output │ └── output.manifest └── temp └── service-use.tmp
一例として、33の静止画です。3を2つ囲ったため、boundingBoxesの要素が2つ存在します。
{ "detectionAnnotations": { "frameNumber": 33, "frame": "frame_0033.jpeg", "boundingBoxes": [ { "label": "three", "labelCategoryAttributes": {}, "width": 123, "top": 200, "height": 204, "left": 406 }, { "label": "three", "labelCategoryAttributes": {}, "width": 126, "top": 205, "height": 195, "left": 549 } ], "frameAttributes": {} } }
ジョブの連鎖
ジョブは連鎖できます。
前回実施したmanifestの内容をそのまま入力に使用することで、未完了のタスクを実施したり、完了したラベル付けの調整・検証が可能です。
追加のデータセットでアノテーションデータを更に増やしたい時も、前回のmanifestに、新規に動画のmanifestの情報を追加することで対応が可能です。
こうして一つのmanifestで教師データを管理しておくと、機械学習を使ったラベリングの自動化を行いたい時に移行が容易になります。
(動画タイプのオブジェクト検出は自動データラベリングが行えないので、静止画タイプに変換する手間が必要となりそうです)
今回は連鎖の挙動のみチェックしていきます。
未完了のタスクの消化
未完了のタスクを消化する為、countDownTeamから連鎖した、countDownTeamChainのジョブを作成します。
完了したジョブから、「ジョブのチェーン」を選びます。
ジョブ名を「countdown-team-chain」として、後は設定を変えずに進めます。
前回のマニフェストを使うことと、cowntdownフォルダ直下に、countdown-team-chainフォルダができることが予想されます。
ジョブの完了後、test3のユーザでタスクを開始してみました。
前回未完了のタスク(1-100)が割り当てられ、test1、test2ユーザには他のジョブが表示されませんでした。
未完了のタスクのみ表示されるようです。
このまま、test3ユーザのラベル付けを完了させると、全てのタスクが完了となり、ジョブが自動で完了しました。
countdown-team-chainフォルダの構成です。
前回のジョブのラベル付けされたデータは継承されず、今回行ったラベル付けのデータのみ反映されています。
. ├── annotation-tool │ └── data.json ├── annotations │ ├── consolidated-annotation │ │ └── output │ │ └── 1 │ │ └── SeqLabel.json │ └── worker-response │ └── iteration-1 │ └── 1 │ ├── 2024-05-09_06:01:44.json │ └── 883dd9b54e2cfa73a8dc97b5ea5d9a13b2b7a29f │ ├── frame_0002.jpeg.json │ ├── frame_0012.jpeg.json │ ├── frame_0022.jpeg.json │ ├── frame_0029.jpeg.json │ ├── frame_0030.jpeg.json │ ├── frame_0031.jpeg.json │ ├── frame_0032.jpeg.json │ ├── frame_0033.jpeg.json │ ├── frame_0034.jpeg.json │ ├── frame_0035.jpeg.json │ ├── frame_0036.jpeg.json │ ├── frame_0037.jpeg.json │ ├── frame_0038.jpeg.json │ ├── frame_0042.jpeg.json │ ├── frame_0052.jpeg.json │ ├── frame_0062.jpeg.json │ ├── frame_0072.jpeg.json │ ├── frame_0082.jpeg.json │ └── frame_0092.jpeg.json ├── manifests │ ├── intermediate │ │ └── 1 │ │ └── output.manifest │ └── output │ └── output.manifest └── temp └── service-use.tmp
完了したタスクの調整
この時点で、全てのタスクが完了しました。
完了したラベル付データのチェックと、必要であれば修正を行うため、 countDownTeamChainから、更に連鎖した、countDownTeamCheckChainの調整ジョブを作成します。
対象がcountDownTeamChainのラベル付けだけなのか、countDownTeamも含まれるのかが気になる所です。
既存ラベルの表示の項目を「調整」で設定します。「検証」を選ぶと、チェックはできますが、編集できません。 他の設定は変えず、ジョブを作成します。
ジョブを開始すると、最も古いcountDownTeamのジョブの内容も含め、ラベル付けのチェックが行えることが解りました。 test2ユーザはタスクは完了にしたものの、実は250までしか実施していなかったので、残していた未着手のラベル付けを行うことができました。
除外のラベルを全てのデータに追加しないと・・・というような需要の時に、便利ですね。
なお、未完了のタスクが残ったジョブから調整ジョブを連鎖すると、エラーが起きて、連鎖したジョブが停止してしまうようです。
先に通常の連鎖を行い、全てのジョブを完了させてから、調整ジョブの連鎖を行ったほうが良さそうですね。
その他調査を含め、分かったこと
- 動画フレームのオブジェクト検出において、ジョブから生成されるタスクは、動画単位となるようだ
- ちなみに、NumberOfHumanWorkersPerDataObjectの値も1で固定で、増やすことはできない
- Releaseしたタスクは、他の人のタスク候補として再度表示され、行うことができる
- Save+Resumeを行うと、タスクを保持したまま保留ができ、後ほど再開できる
- Declineしたタスクは、終了とみなされ、タスク候補として表示されなくなるが、連鎖したジョブでは再びタスク候補に表示される
- 未完了のタスクを持って完了したジョブを連鎖すると、新しいジョブで未完了のタスクを処理できる
- 連鎖したジョブのoutput.manifestには、そのジョブで行ったラベル付けのデータのみ反映される
まとめ
ドキュメントだけでは難解な部分もあるGround Truthですが、実際に手を動かすことで、使い勝手の良いツールであることが解りました。
ラベル付けのデータがジョブ毎に分散してしまう所は気になっていて、複数のジョブから簡単にラベル付けデータの集約ができる仕組みが必要だなと感じました。